package com.ymh.config;

import java.security.Principal;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @author: 13590
 * @date: 2020/11/8 19:19
 * @description:
 */
@Component
@Slf4j
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

  @Autowired
  private RemoteTokenServices remoteTokenServices;

  private static final List<String> WHITE_URL_LIST = Arrays
      .asList("/auth/oauth/check_token", "/auth/oauth/token");

  private static final PathMatcher PATH_MATCHER = new AntPathMatcher();

  @Override
  public Mono<AuthorizationDecision> check(Mono<Authentication> authentication,
      AuthorizationContext object) {
    ServerWebExchange exchange = object.getExchange();
    String requestPath = exchange.getRequest().getURI().getPath();
    log.debug("当前请求路径：{}", requestPath);
    if (isInWhiteList(requestPath)) {
      // 白名单直接放行
      return Mono.just(new AuthorizationDecision(true));
    }
    String accessToken = exchange.getRequest().getHeaders().getFirst("Authorization");
    log.debug("当前请求头Authorization中的值:{}", accessToken);
    if (StringUtils.isBlank(accessToken)) {
      log.warn("当前请求头Authorization中的值不存在");
      return Mono.just(new AuthorizationDecision(false));
    }
    accessToken = accessToken.replace(OAuth2AccessToken.BEARER_TYPE + " ", "");
    OAuth2Authentication oAuth2Authentication = remoteTokenServices.loadAuthentication(accessToken);

    log.debug("当前token所拥有的OAuth2Authentication:{}", oAuth2Authentication);
    Collection<GrantedAuthority> authorities = oAuth2Authentication.getAuthorities();
    log.debug("当前token所拥有的权限:{}", authorities.toString());

    for (GrantedAuthority authority : authorities) {
      String authorityStr = authority.getAuthority();
      if (StringUtils.equals("ROLE_ADMIN", authorityStr) || PATH_MATCHER
          .match(authorityStr, requestPath)) {
        //设置请求头参数username
        ServerHttpRequest request = exchange.getRequest().mutate()
            .headers(httpHeaders -> httpHeaders.add("username", oAuth2Authentication.getName()))
            .build();
        exchange.mutate().request(request).build();
        return Mono.just(new AuthorizationDecision(true));
      }
    }
    return Mono.just(new AuthorizationDecision(false));
  }

  private boolean isInWhiteList(String requestPath) {
    for (String whiteUrl : WHITE_URL_LIST) {
      if (PATH_MATCHER.match(whiteUrl, requestPath)) {
        log.debug("url: {}, path: {},白名单地址直接放行", whiteUrl, requestPath);
        return true;
      }
    }
    return false;

  }

}
